package cn.android.support.v7.lib.sin.crown.kotlin.socket

import android.util.Log
import cn.android.support.v7.lib.sin.crown.utils.LoggerUtils
import kotlinx.coroutines.experimental.async
import java.io.DataInputStream
import java.io.DataOutputStream
import java.net.Socket
import android.app.Activity
import java.net.InetSocketAddress


/**
 * 客户端
 */
class TcpClientSocket(var socket: Socket? = null) {

    companion object {
        interface ConnectSocketCallBack {
            //开始链接
            fun onStart(ip: String, port: Int, ipPort: String = ip + ":" + port)

            //链接成功
            fun onSuccess(it: TcpClientSocket)

            //链接失败,reason是失败的原因
            fun onFail(ip: String, port: Int, ipPort: String = ip + ":" + port, reason: String)

            //已经链接，不会再执行以上方法
            fun onAlready(it: TcpClientSocket)
        }

        //根据ip和prot存储客户端
        var tcpMap = mutableMapOf<String, TcpClientSocket>()

        /**
         * 链接服务器，输入ip地址和端口号
         * "0.0.0.0" 这个ip是本机。只对运行在同一个机器上才有效。
         */
        fun connect(activity: Activity? = null, ip: String = TcpServerSocket.Ip, port: Int = TcpServerSocket.Port, timeout: Int = 5000, callbak: ConnectSocketCallBack? = null): TcpClientSocket? {
            var IpPort = ip + ":" + port
            async {
                if (tcpMap.get(IpPort)?.isConnect() ?: false) {
                    //fixme 已经链接
                    activity?.runOnUiThread {
                        callbak?.onAlready(tcpMap.get(IpPort)!!)
                    } ?: callbak?.onAlready(tcpMap.get(IpPort)!!)

                } else {
                    //未链接，进行链接
                    var socket = Socket()
                    val socAddress = InetSocketAddress(ip, port)
                    var reason=""//链接失败的原因
                    try {
                        //fixme activity不为空时，跳转到UI主线程。主要用于线程跳转
                        //fixme 开始链接
                        activity?.runOnUiThread {
                            callbak?.onStart(ip, port)
                        } ?: callbak?.onStart(ip, port)
                        socket.soTimeout = timeout//设置read方法的超时时间。单位毫秒。1000等于1秒
                        //注意，必须进行异常捕捉。超时设置才有效。不捕捉则超时设置无效。仍然会卡半天。
                        //设置链接超时时间，单位毫秒。1000等于1秒
                        socket.connect(socAddress, timeout)//fixme 这一步，链接的过程中，是阻塞线程的。
                    } catch (e: Exception) {
                        reason=e.message.toString()
                        Log.e("test", "链接超时异常:\t" + e.message)
                    }
                    //var socket=Socket(ip, port) 这样直接连接，如果ip不存在，会卡在这里老半天。然后跑出异常
                    var tcpCustomerSocket = TcpClientSocket(socket)
                    //回调，返回 TcpCustomerSocket 和 是否链接成功。成功返回true
                    if (tcpCustomerSocket.isConnect()) {
                        tcpMap.put(IpPort, tcpCustomerSocket)//保存已链接的客户端。
                        //fixme 成功回调
                        activity?.runOnUiThread {
                            callbak?.onSuccess(tcpCustomerSocket)
                        } ?: callbak?.onSuccess(tcpCustomerSocket)
                    } else {
                        tcpMap.remove(IpPort)//移除未链接客户端
                        //fixme 链接失败
                        activity?.runOnUiThread {
                            callbak?.onFail(ip, port,reason=reason)
                        } ?: callbak?.onFail(ip, port,reason=reason)
                    }
                }
            }
            return tcpMap?.get(IpPort) ?: null
        }
    }

    //获取IP
    fun getIp(): String? {
        return socket?.inetAddress?.hostAddress
    }

    //获取端口号
    fun getPort(): Int? {
        return socket?.port
    }

    //获取ip地址+端口号port 形式为 192.168.0.1:8889
    fun getIpPort(): String? {
        return getIp() + ":" + getPort()
    }

    //获取远程的IP地址和端口， 形式为 192.168.0.1:8889
    fun getRemoteIpPort(): String? {
        return socket?.remoteSocketAddress?.toString()
    }

    //向对方写入信息
    fun writeUTF(str: String): TcpClientSocket {
        socket?.let {
            async {
                try {
                    if (!isClose()) {//判断链接是否关闭和链接
                        var out = DataOutputStream(socket?.getOutputStream())
                        out.writeUTF(str)//向对方写入信息
                        out.flush()
                    } else {
//                        Log.e("test", "写入失败,链接已断开")
                    }
                } catch (e: Exception) {
                    Log.e("test", "向对方写入信息异常:\t" + e.message)
                }
            }
        }
        return this
    }

    //读取对方信息(监听状态，需要等待对方写入)
    fun readUTF(activity: Activity? = null, callbak: (str: String) -> Unit): TcpClientSocket {
        socket?.let {
            async {
                try {
                    while (!isClose()) {//判断链接是否关闭和链接
                        var input = DataInputStream(socket?.getInputStream())
                        var str = input.readUTF()//读要等待写的完成，不然一直处于等待状态。
                        activity?.runOnUiThread {
                            callbak(str)
                        } ?: callbak(str)
                        //LoggerUtils.e("test", "来自客户端信息:\t" + str)
                    }
                } catch (e: Exception) {
                    Log.e("test", "读取对方信息异常:\t" + e.message)
                }
            }
        }
        return this
    }

    //判断链接是否关闭, true 关闭，fales 没有关闭
    fun isClose(): Boolean {
        socket?.let {
            if (!socket!!.isClosed && socket!!.isConnected) {
                return false
            }
        }
        return true
    }

    //判断链接是否成功
    fun isConnect(): Boolean {
        socket?.let {
            if (!socket!!.isClosed && socket!!.isConnected) {
                return true
            }
        }
        return false
    }

    //关闭链接
    fun close(): TcpClientSocket {
        socket?.let {
            try {
                if (socket!!.isInputShutdown) {
                    socket!!.shutdownInput()//关闭输入流
                }
                if (socket!!.isOutputShutdown) {
                    socket!!.shutdownOutput()//关闭输出流
                }
                if (!isClose()) {
                    socket!!.close()//关闭
                } else {
                    //Log.e("test", "socket已经关闭")
                }
            } catch (e: Exception) {
                Log.e("test", "socket关闭异常:\t" + e.message)
            }
        }
        return this
    }
}